/* Unexec for the Archimedes.
 *
 * This routine saves a "snapshot" of the running program, in a form
 * suitable for later running as a RISC OS application. The whole of the
 * executing image is saved, but NOT the heap or the stack, so that pointers
 * to heap allocated space, or automatic variables, will be garbage when the
 * process is restarted.
 */

#include <assert.h>
#include <stdio.h>
#include "kernel.h"
#include "unexec.h"

/* This lot are really addresses (void *), but we call them unsigned
 * integers here (unlike in the "utils.h" header file), as that is how
 * we will be using them. nasty, but it saves a lot of casts...
 */

extern unsigned int _RO_Base;
extern unsigned int _RO_Limit;
extern unsigned int _RW_Base;
extern unsigned int _RW_Limit;
extern unsigned int _ZI_Base;
extern unsigned int _ZI_Limit;

extern unsigned int _Stub_Entries_Base;
extern unsigned int _Stub_Entries_Limit;
extern unsigned int _Stub_Data_Base;
extern unsigned int _Stub_Data_Limit;

void unexec (char *file)
{
	int fd;
	int offset;
	int length;
	_kernel_osfile_block blk;

	/* Make sure the necessary assumptions are valid! */
	assert(_RO_Limit == _RW_Base);
	assert(_ZI_Base == _ZI_Limit);
	assert(_RO_Base <= _RW_Limit);
	assert(_RO_Base == 0x8080);

	blk.load = 0xFF8;
	blk.exec = 0;
	blk.start = 0x8000;
	blk.end = _RW_Limit;
	_kernel_osfile(10,file,&blk);

	/* *******************************************************
	 * The following code is HIGHLY dependent on the exact
	 * format of the shared C library stubs! It is probably
	 * not necessary, either! If in doubt, comment it out!
	 * Based on Shared C Library stubs 3.50 (supplied with
	 * Acorn C v3.0).
	 * It has been visually verified with stubs 3.75 (the
	 * version supplied with Desktop C v4.0), and appears
	 * to be OK with this, too.
	 * *******************************************************/

	/* Now open the dumped image to zap the C Library stubs data */
	fd = _kernel_osfind(0xC7,file);

	/* If we can't open it for update, leave it as it is */
	if (fd == 0)
		return;

	/* Set Stub$$Entries to 0xE3A0F000 (repeated) */
	offset = _Stub_Entries_Base - 0x8000;
	length = (_Stub_Entries_Limit - _Stub_Entries_Base);

	/* Must be an exact number of words */
	assert((length & 0x03) == 0);

	/* Go to the area start (give up if we cannot) */
	if (_kernel_osargs(1,fd,offset) == _kernel_ERROR)
		goto quit;

	while (length > 0)
	{
		_kernel_osbput(0x00,fd);
		--length;
		_kernel_osbput(0xF0,fd);
		--length;
		_kernel_osbput(0xA0,fd);
		--length;
		_kernel_osbput(0xE3,fd);
		--length;
	}

	/* Zero out Stub$$Data */
	offset = _Stub_Data_Base - 0x8000;
	length = (_Stub_Data_Limit - _Stub_Data_Base);

	/* Go to the area start (give up if we cannot) */
	if (_kernel_osargs(1,fd,offset) == _kernel_ERROR)
		goto quit;

	while (length > 0)
	{
		_kernel_osbput(0,fd);
		--length;
	}

	/* We are done. Close the file */
quit:
	_kernel_osfind(0,(char *)fd);
}
